﻿namespace ElisVK.Utils.HotKeys
{
    using System;
    //using System.Diagnostics.Contracts;
    using System.Windows;
    using System.Windows.Forms;
    using System.Windows.Input;
    using System.Windows.Interop;

    [Serializable]
    public sealed class HotKey : IDisposable
    {
        public event Action<HotKey> HotKeyPressed;

        private int _id;
        private bool _isKeyRegistered;
        private IntPtr _handle;

        public HotKey()
        {
            ComponentDispatcher.ThreadPreprocessMessage += ThreadPreprocessMessageMethod;
        }

        public HotKey(string name, ModifierKeys modifierKeys, Keys key)
        {
            //Contract.Requires(modifierKeys != ModifierKeys.None || key != Keys.None);

            Name = name;
            Key = key;
            KeyModifier = modifierKeys;
            _id = GetHashCode();
            ComponentDispatcher.ThreadPreprocessMessage += ThreadPreprocessMessageMethod;
        }

        ~HotKey()
        {
            Dispose();
        }

        public string Name { get; set; }

        public Keys Key { get; set; }

        public ModifierKeys KeyModifier { get; set; }

        private int ID
        {
            get
            {
                if (_id == 0)
                    _id = GetHashCode();

                return _id;
            }
        }

        public void RegisterHotKey(Window window)
        {
            if (Key == Keys.None)
                return;

            _handle = (new WindowInteropHelper(window)).Handle;

            if (_isKeyRegistered)
                UnregisterHotKey();

            _isKeyRegistered = HotKeyWinApi.RegisterHotKey(_handle, ID, KeyModifier, Key);

            if (!_isKeyRegistered)
                throw new ApplicationException("Hotkey already in use");
        }

        public void UnregisterHotKey()
        {
            _isKeyRegistered = !HotKeyWinApi.UnregisterHotKey(_handle, ID);
        }

        public void Dispose()
        {
            ComponentDispatcher.ThreadPreprocessMessage -= ThreadPreprocessMessageMethod;
            UnregisterHotKey();
        }

        private void ThreadPreprocessMessageMethod(ref MSG msg, ref bool handled)
        {
            if (!handled)
            {
                if (msg.message == HotKeyWinApi.WmHotKey
                    && (int)(msg.wParam) == ID)
                {
                    OnHotKeyPressed();
                    handled = true;
                }
            }
        }

        private void OnHotKeyPressed()
        {
            if (HotKeyPressed != null)
                HotKeyPressed(this);
        }
    }
}
